package com.yc.psychologicalconsultant.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yc.psychologicalconsultant.config.SmsConfig;
import com.yc.psychologicalconsultant.pojo.Result;
import com.yc.psychologicalconsultant.service.EmailService;
import com.yc.psychologicalconsultant.service.SmsService;
import com.yc.psychologicalconsultant.utils.JwtUtils;
import com.yc.psychologicalconsultant.utils.RandomUtils;
import com.yc.psychologicalconsultant.utils.UUIDUtil;
import com.yc.psychologicalconsultant.vo.*;
import com.yc.psychologicalconsultant.dao.UserDAO;
import com.yc.psychologicalconsultant.pojo.User;
import com.yc.psychologicalconsultant.service.UserService;
import com.yc.psychologicalconsultant.utils.MD5Util;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserDAO, User> implements UserService {
    @Resource
    private MD5Util md5Util;
    @Resource
    private UserDAO userDAO;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private SmsService smsService;
    @Resource
    private EmailService emailService;

    /**
     * 用户名 + 密码登录
     */
    @Override
    public Result loginByUsername(LoginUserNameVO loginUserNameVO) {
        User user =
                userDAO.selectOne(new QueryWrapper<User>().
                        eq("username", loginUserNameVO.getUsername())
                        .eq("password", md5Util.getMD5(loginUserNameVO.getPassword())));
        if (user != null) {
            log.info("登录用户成功");
            List<String> list = new ArrayList<>();
            list.add(user.getPhone());
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", user.getId());
            claims.put("username", user.getUsername());
            String parseJWT = JwtUtils.generateJwt(claims);
            list.add(user.getUsername());
            list.add(user.getAvatar());
            list.add(parseJWT);
            // 删除以username开头的所有键
            deleteKeysWithPrefixUsingScan(user.getUsername());
            return Result.success(list);
        }
        log.info("登录失败");
        return Result.error("账号或密码错误");
    }

    @Override
    public Result loginByEmail(LoginEmailVO loginEmailVO) {
        // 1. 验证 验证码 与 DB 中的是否一样
        // 验证 验证码是否正确
        String code = (String) redisTemplate.opsForValue().get("verification-email:" + loginEmailVO.getEmail());
        if (!Objects.equals(loginEmailVO.getCode(), code)) return Result.error("验证码错误");

        // 2. 查看DB 中是否有邮箱 为 xxx 的用户
        User v2 = userDAO.selectOne(new QueryWrapper<User>().
                eq("email", loginEmailVO.getEmail()));
        if (v2 == null) return Result.error("邮箱不再存在");

        // 3. 验证码正确后直接删除 key
        redisTemplate.delete("verification-email:" + loginEmailVO.getEmail());

        log.info("登录用户成功");
        List<String> list = new ArrayList<>();
        list.add(v2.getUsername());
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", v2.getId());
        claims.put("username", v2.getUsername());
        claims.put("email", v2.getEmail());
        String parseJWT = JwtUtils.generateJwt(claims);
        list.add(parseJWT);
        // 删除以username开头的所有键
        deleteKeysWithPrefixUsingScan(v2.getUsername());
        return Result.success(list);
    }

    @Override
    public Result registerByEmail(UserRegisterByEmailVO userEmailVO) {
        // 验证 验证码是否正确
        String code = (String) redisTemplate.opsForValue().get("verification-email:" + userEmailVO.getEmail());
        if (!Objects.equals(userEmailVO.getCode(), code)) return Result.error("验证码不正确");

        // 检验用户名是否存在
        User usernameUnique = userDAO.selectOne(new QueryWrapper<User>().
                eq("username", userEmailVO.getUsername()));
        if (usernameUnique != null) return Result.error("用户名已存在");

        // 检验邮箱是否存在
        User emailUnique = userDAO.selectOne(new QueryWrapper<User>().
                eq("email", userEmailVO.getEmail()));
        if (emailUnique != null) return Result.error("邮箱已存在");

        // 验证码正确后直接删除 key
        redisTemplate.delete("verification-email:" + userEmailVO.getEmail());

        // 重新设置md5加密加盐后的密码
        userEmailVO.setPassword(md5Util.getMD5(userEmailVO.getPassword()));

        // 将 VO 与 User 合并
        String uuid = UUIDUtil.uuid();
        User user = new User();
        BeanUtils.copyProperties(userEmailVO, user);
        user.setCreateTime(LocalDateTime.now());
        user.setId(uuid);

        // 注册成功
        userDAO.insert(user);
        return Result.success("注册成功");
    }

    /**
     * 通过手机号 登录/注册
     * 1. 校验验证码是否正确
     * 2. 检验手机号是否注册过(注册过 => 登录 // 没有 => 注册)
     */
    @Override
    public Result loginOrRegisterByPhone(LoginPhoneVO userPhoneVO) {
        // 检验验证码是否正确
        String code = userPhoneVO.getCode();
        String codeFromRedis = (String) redisTemplate.opsForValue().
                get("verification-phone:" + userPhoneVO.getPhone());
        if (!Objects.equals(code, codeFromRedis)) return Result.error("验证码错误");

        // 检验手机号是否存在
        User loginUser = userDAO.selectOne(new QueryWrapper<User>().
                eq("phone", userPhoneVO.getPhone()));

        // 验证码正确后直接删除 key
        redisTemplate.delete("verification-phone:" + userPhoneVO.getPhone());

        // 存在则直接返回
        if (loginUser != null) {
            log.info("登录用户成功");
            List<String> list = new ArrayList<>();
            list.add(loginUser.getPhone());
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", loginUser.getId());
            claims.put("phone", loginUser.getPhone());
            String parseJWT = JwtUtils.generateJwt(claims);
            list.add(loginUser.getUsername());
            list.add(loginUser.getAvatar());
            list.add(parseJWT);
            return Result.success(list);
        }

        // 如果手机号不存在, 则注册新用户
        // 将 VO 与 User 合并
        String uuid = UUIDUtil.uuid();
        User user = new User();
        BeanUtils.copyProperties(userPhoneVO, user);
        user.setCreateTime(LocalDateTime.now());
        user.setUsername("用户:" + user.getPhone() + uuid.substring(0, 4));
        user.setId(uuid);
        user.setAvatar("https://web-tias-fc.oss-cn-chengdu.aliyuncs.com/82eeea3b-46de-4d2e-b5c5-2f5d4575a003.jpg");

        // 注册成功
        userDAO.insert(user);

        log.info("登录用户成功");
        List<String> list = new ArrayList<>();
        list.add(user.getPhone());
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", user.getId());
        claims.put("phone", user.getPhone());
        String parseJWT = JwtUtils.generateJwt(claims);
        list.add(parseJWT);
        list.add(user.getAvatar());
        list.add(user.getUsername());
        // 删除其存在的key
        deleteKeysWithPrefixUsingScan(user.getUsername());
        return Result.success(list);
    }

    @Override
    public Result getEmailCode(VerificationEmailVO verificationEmailVO) {
        String email = verificationEmailVO.getEmail();
        User user = this.getOne(
                new QueryWrapper<User>().eq("email", email));

        // 注册验证码
        if (user == null) {
            // 查看是否验证码是否已存在(一分钟内不得重复发验证码)
            String code = (String) redisTemplate.opsForValue().
                    get("verification-email-register:" + email);
            if (code != null) return Result.error("一分钟内只能获取一次验证码,请稍后再试");
        } else {
            String code = (String) redisTemplate.opsForValue().
                    get("verification-email-login:" + email);
            if (code != null) return Result.error("一分钟内只能获取一次验证码,请稍后再试");
        }

        //发送邮件
        String code = RandomUtils.codeForEmail(4);

        //将验证码存入redis中
        // 失效时间1分钟 (防止重复发)
        if (user == null) {
            redisTemplate.opsForValue().set("verification-email-register:"
                    + email, code, 1, TimeUnit.MINUTES);
        } else {
            redisTemplate.opsForValue().set("verification-email-login:"
                    + email, code, 1, TimeUnit.MINUTES);
        }
        redisTemplate.opsForValue().set("verification-email:" + verificationEmailVO.getEmail(), code, 5, TimeUnit.MINUTES);

        String title = "用户验证";
        try {
            emailService.mail(email, verificationEmailVO.getUsername(), title, code);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return Result.success("验证码获取成功", code);
    }

    @Override
    public Result getPhoneCode(String phone) {
        User user = this.getOne(
                new QueryWrapper<User>().eq("phone", phone));
        // 注册验证码
        if (user == null) {
            // 查看是否验证码是否已存在(一分钟内不得重复发验证码)
            String code = (String) redisTemplate.opsForValue().
                    get("verification-phone-register:" + phone);
            if (code != null) return Result.error("一分钟内只能获取一次验证码,请稍后再试");
        } else {
            String code = (String) redisTemplate.opsForValue().
                    get("verification-phone-login:" + phone);
            if (code != null) return Result.error("一分钟内只能获取一次验证码,请稍后再试");
        }
        //生成验证码
        Map<String, Object> map = new HashMap<>();
        String code = RandomUtils.getFourBitRandom();
        log.info("验证码:{}", code);
        map.put("code", code);

        // 发送阿里云短信验证码
        smsService.send(phone, SmsConfig.TEMPLATE_CODE, map);

        // 将验证码存入redis中
        // 失效时间1分钟 (防止重复发)
        if (user == null) {
            redisTemplate.opsForValue().set("verification-phone-register:"
                    + phone, code, 1, TimeUnit.MINUTES);
        } else {
            redisTemplate.opsForValue().set("verification-phone-login:"
                    + phone, code, 1, TimeUnit.MINUTES);
        }
        // 失效时间5分钟
        redisTemplate.opsForValue().set("verification-phone:"
                + phone, code, 5, TimeUnit.MINUTES);

        return Result.success("获取验证码成功", code);
    }

    @Override
    public Result updateAvatar(String userId, String url) {
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("id", userId);
        User user = this.getOne(wrapper);
        user.setAvatar(url);
        return this.updateById(user) ? Result.success("个人信息更新成功") : Result.error("个人信息更新失败");
    }

    @Override
    public Result searchUser(String username) {
        UserOnLine onlineUser = userDAO.getOnlineUser(username);
        if (onlineUser == null) return Result.error("未找到该用户");
        return Result.success(onlineUser);
    }

    /**
     * 删除所有以username开头的键
     * 用于删除聊天记录等
     */
    public void deleteKeysWithPrefixUsingScan(String username) {
        String pattern = username + "*";
        ScanOptions options = ScanOptions.scanOptions().match(pattern).count(100).build();
        redisTemplate.execute((RedisConnection connection) -> {
            Cursor<byte[]> cursor = connection.scan(options);
            while (cursor.hasNext()) {
                String key = new String(cursor.next());
                redisTemplate.delete(key);
            }
            return null;
        });
    }


}
